﻿using System.Collections.Generic;
using Newtonsoft.Json;

namespace DotnetUtil.Extensions
{
    public static class CloneExtension
    {
        /// <summary>
        /// 使用Json实现深拷贝
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="source">原始对象</param>
        /// <returns>深拷贝对象</returns>
        public static T CloneJson<T>(this T source)
        {
            //不序列化空对象，对象为空时简单返回目标类型的缺省对象
            if (ReferenceEquals(source, null))
            {
                return default;
            }

            var deserializeSettings = new JsonSerializerSettings
            {
                ObjectCreationHandling = ObjectCreationHandling.Replace
            };

            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
        }

        /// <summary>
        /// 通过反射实现深拷贝
        /// </summary>
        /// <typeparam name="TSource">原始类型</typeparam>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="source">原始对象</param>
        /// <param name="destination">目标对象</param>
        public static void CloneReflection<TSource, TDestination>(TSource source, TDestination destination)
            where TSource : class
            where TDestination : class
        {
            if (source == null || destination == null)
            {
                return;
            }

            var sourceType = typeof(TSource);
            var destinationType = typeof(TDestination);

            var properties = sourceType.GetProperties();
            foreach (var sourceProperty in properties)
            {
                var destinationProperty = destinationType.GetProperty(sourceProperty.Name);
                if (destinationProperty != null && destinationProperty.CanWrite && sourceProperty.CanRead)
                {
                    destinationProperty.SetValue(destination, sourceProperty.GetValue(source));
                }
            }
        }

        /// <summary>
        /// 通过反射实现深拷贝
        /// </summary>
        /// <typeparam name="TSource">原始类型</typeparam>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="sourceList">原始对象</param>
        /// <param name="destinationList">目标对象</param>
        public static void CloneReflection<TSource, TDestination>(List<TSource> sourceList,
            List<TDestination> destinationList)
            where TSource : class
            where TDestination : class, new()
        {
            var sourceType = typeof(TSource);
            var destinationType = typeof(TDestination);

            var properties = sourceType.GetProperties();

            foreach (var source in sourceList)
            {
                foreach (var sourceProperty in properties)
                {
                    var obj = new TDestination();
                    var destinationProperty = destinationType.GetProperty(sourceProperty.Name);
                    if (destinationProperty != null && destinationProperty.CanWrite && sourceProperty.CanRead)
                    {
                        destinationProperty.SetValue(obj, sourceProperty.GetValue(source));
                    }
                }
            }
        }

        /// <summary>
        /// 通过反射实现深拷贝
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="source">原始对象</param>
        /// <param name="destination">目标对象</param>
        public static void CloneReflection<T>(this T source, T destination) where T : class
        {
            if (source == null || destination == null)
            {
                return;
            }

            var type = typeof(T);
            var properties = type.GetProperties();
            foreach (var property in properties)
            {
                if (property.CanRead && property.CanWrite)
                {
                    property.SetValue(destination, property.GetValue(source));
                }
            }
        }

        /// <summary>
        /// 通过反射实现深拷贝
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="source">原始对象</param>
        public static T CloneReflection<T>(this T source) where T : class, new()
        {
            if (source == null)
            {
                return null;
            }

            var destination = new T();

            var type = typeof(T);
            var properties = type.GetProperties();
            foreach (var property in properties)
            {
                if (property.CanRead && property.CanWrite)
                {
                    property.SetValue(destination, property.GetValue(source));
                }
            }

            return destination;
        }
    }
}
